package com.hksj.lntm.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.hksj.cbm.entity.DeviceInstance;
import com.hksj.cbm.entity.DeviceMetadataParseInfo;
import com.hksj.cbm.mapper.DeviceMetadataParseInfoMapper;
import com.hksj.cbm.service.DeviceInstanceService;
import com.hksj.common.core.common.Constants;
import com.hksj.common.core.common.DevicePropertyConstants;
import com.hksj.common.core.common.SpecialityModelCodeEnum;
import com.hksj.common.core.enums.DeviceState;
import com.hksj.common.core.util.DateUtil;
import com.hksj.lntm.dto.LiquidNitrogenAlarmDetailDTO;
import com.hksj.lntm.dto.LiquidNitrogenCockpitDTO;
import com.hksj.lntm.dto.LiquidNitrogenDeviceDTO;
import com.hksj.lntm.mapper.LiquidNitrogenAlarmMapper;
import com.hksj.lntm.mapper.LiquidNitrogenCockpitMapper;
import com.hksj.lntm.mapper.LiquidNitrogenDeviceMapper;
import com.hksj.lntm.vo.*;
import com.hksj.tdengine.taos.service.TaosService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author fjt
 * @data 2024/11/12 9:38
 * @describe
 */
@Slf4j
@Service
public class LiquidNitrogenCockpitService {


    @Autowired
    private LiquidNitrogenCockpitMapper liquidNitrogenCockpitMapper;
    @Autowired
    private LiquidNitrogenAlarmMapper liquidNitrogenAlarmMapper;
    @Autowired
    private LiquidNitrogenDeviceMapper liquidNitrogenDeviceMapper;
    @Autowired
    private DeviceMetadataParseInfoMapper deviceMetadataParseInfoMapper;
    @Autowired
    private DeviceInstanceService deviceInstanceService;
    @Autowired
    private TaosService taosService;


    /**
     * 告警记录详情（最近7天）
     * @param dto
     * @return
     */
    public List<LiquidNitrogenAlarmDeviceInfoVO> queryAlarmList(LiquidNitrogenCockpitDTO dto){
        LiquidNitrogenAlarmDetailDTO detailDTO = LiquidNitrogenAlarmDetailDTO.builder()
                .paths(dto.getPaths()).departmentItemValue(dto.getDepartmentItemValue())
                .code(SpecialityModelCodeEnum.liquid_nitrogen.getCode()).build();
        detailDTO.setEndTime(DateUtil.getYmdHms(DateUtil.getYmd() + " 23:59:59"));
        detailDTO.setStartTime(DateUtil.getYmdHms(DateUtil.getAfterDate(DateUtil.getYmd(), -6, DateUtil.DEFAULT_FORMAT_DATE) + " 00:00:00"));
        return liquidNitrogenAlarmMapper.queryAlarmDetail(detailDTO, null);
    }


    /**
     * 温度告警次数趋势（最近7天）
     * @param dto
     * @return
     */
    public List<LiquidNitrogenTemperatureAlarmNumTrendVO> queryAlarmTrend(LiquidNitrogenCockpitDTO dto){
        dto.setCode(SpecialityModelCodeEnum.liquid_nitrogen.getCode());
        dto.setEndTime(DateUtil.getYmdHms(DateUtil.getYmd() + " 23:59:59"));
        dto.setStartTime(DateUtil.getYmdHms(DateUtil.getAfterDate(DateUtil.getYmd(), -6, DateUtil.DEFAULT_FORMAT_DATE) + " 00:00:00"));
        dto.setProperty(DevicePropertyConstants.liquid_nitrogen_temperature);
        List<LiquidNitrogenTemperatureAlarmNumTrendVO> list = liquidNitrogenCockpitMapper.queryAlarmTrend(dto);
        Map<LocalDate, Integer> map = new HashMap<>();
        if (CollUtil.isNotEmpty(list)){
            map = list.stream().collect(Collectors.toMap(p -> p.getCreateTime(), p -> p.getNum()));
        }

        // 获取指定时间段内的每一天
        List<LocalDate> dateList = DateUtil.getDatesInRange(DateUtil.convertToLocalDate(dto.getStartTime()), DateUtil.convertToLocalDate(dto.getEndTime()));
        List<LiquidNitrogenTemperatureAlarmNumTrendVO> voList = new ArrayList<>(dateList.size());
        Map<LocalDate, Integer> finalMap = map;
        dateList.forEach(localDate -> {
            int num = ObjectUtil.isNotNull(finalMap.get(localDate)) ? finalMap.get(localDate) : 0;
            LiquidNitrogenTemperatureAlarmNumTrendVO trendVO = LiquidNitrogenTemperatureAlarmNumTrendVO.builder()
                    .createTime(localDate).num(num).build();
            voList.add(trendVO);
        });
        // 按时间升序排序
        return voList.stream().sorted(Comparator.comparing(LiquidNitrogenTemperatureAlarmNumTrendVO::getCreateTime, Comparator.naturalOrder()))
                .collect(Collectors.toList());
    }


    /**
     * 查询当前报警设备信息
     * @param dto
     * @return
     */
    public LiquidNitrogenCurrentAlarmDeviceInfoVO queryCurrentAlarmDevice(LiquidNitrogenCockpitDTO dto){
        LiquidNitrogenCurrentAlarmDeviceInfoVO deviceInfoVO = new LiquidNitrogenCurrentAlarmDeviceInfoVO();
        dto.setCode(SpecialityModelCodeEnum.liquid_nitrogen.getCode());
        // 查询在线设备数量
        Integer onlineNum = liquidNitrogenCockpitMapper.queryOnlineNum(dto);
        deviceInfoVO.setOnlineNum(onlineNum);
        // 查询当前报警中的设备id和属性
        dto.setState(DeviceState.online.getValue());
        List<LiquidNitrogenCurrentAlarmDeviceVO> deviceList = liquidNitrogenCockpitMapper.queryCurrentAlarmDeviceIdAndProperty(dto);
        if (CollUtil.isEmpty(deviceList)){
            deviceInfoVO.setTemperatureAlarmNum(0);
            deviceInfoVO.setDepthAlarmNum(0);
            return deviceInfoVO;
        }
        Map<String, List<String>> map = deviceList.stream().collect(
                Collectors.groupingBy(LiquidNitrogenCurrentAlarmDeviceVO::getProperty,
                        Collectors.mapping(LiquidNitrogenCurrentAlarmDeviceVO::getDeviceId, Collectors.toList())
                ));

        // 温度报警的设备数量
        List<String> tempDeviceIdList = map.get(DevicePropertyConstants.liquid_nitrogen_temperature);
        deviceInfoVO.setTemperatureAlarmNum(CollUtil.isNotEmpty(tempDeviceIdList) ? tempDeviceIdList.size() : 0);
        // 液位深度报警的设备数量
        List<String> depthDeviceIdList = map.get(DevicePropertyConstants.liquid_nitrogen_depth);
        deviceInfoVO.setDepthAlarmNum(CollUtil.isNotEmpty(depthDeviceIdList) ? depthDeviceIdList.size() : 0);

        // 查询在指定的设备集合中，当前处于报警中的设备情况
        List<String> deviceIdList = deviceList.stream().map(LiquidNitrogenCurrentAlarmDeviceVO::getDeviceId).collect(Collectors.toList());
        List<LiquidNitrogenAlarmDeviceInfoVO> alarmDeviceInfoList = liquidNitrogenCockpitMapper.queryCurrentAlarmDeviceInfoList(deviceIdList);
        deviceInfoVO.setAlarmDeviceInfoList(alarmDeviceInfoList);
        return deviceInfoVO;
    }


    /**
     * 驾驶舱设备列表
     * @param dto
     * @return
     */
    public List<LiquidNitrogenDeviceVO> queryLiquidNitrogenDeviceList(LiquidNitrogenCockpitDTO dto){
        // 查询符合要求的设备
        LiquidNitrogenDeviceDTO deviceDTO = LiquidNitrogenDeviceDTO.builder()
                .paths(dto.getPaths()).departmentItemValue(dto.getDepartmentItemValue())
                .code(SpecialityModelCodeEnum.liquid_nitrogen.getCode()).build();
        List<LiquidNitrogenDeviceVO> list = liquidNitrogenDeviceMapper.queryLiquidNitrogenDeviceByPage(null, deviceDTO);
        if (CollUtil.isEmpty(list)){
            return null;
        }
        List<String> deviceIdList = list.stream().map(LiquidNitrogenDeviceVO::getDeviceId).collect(Collectors.toList());

        // 2.获取每个设备检测指标的当前值
        LambdaQueryWrapper<DeviceMetadataParseInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(DeviceMetadataParseInfo::getDeviceId, deviceIdList);
        List<DeviceMetadataParseInfo> deviceMetadataParseInfoList = deviceMetadataParseInfoMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(deviceMetadataParseInfoList)){
            return list;
        }
        // 设备id : List<DeviceMetadataParseInfo>
        Map<String, List<DeviceMetadataParseInfo>> deviceMetadataParseInfoMap = deviceMetadataParseInfoList.stream().collect(
                Collectors.groupingBy(DeviceMetadataParseInfo::getDeviceId));

        // 3.查询每个设备监测指标的报警情况
        List<PropertyModelVO> propertyModelVOList = liquidNitrogenDeviceMapper.queryAlarmByDeviceIdList(deviceIdList);
        Map<String, Integer> deviceAlarmStatusMap = new HashMap<>();
        if (CollUtil.isNotEmpty(propertyModelVOList)){
            deviceAlarmStatusMap = propertyModelVOList.stream().collect(
                    Collectors.toMap(r -> r.getDeviceId() + "-" + r.getProperty(), r -> r.getStatus()));
        }

        // 4.查询每个设备的额度液位深度  设备id-设备物模型中标签定义的key : 值
        Map<String, BigDecimal> paramMap = queryLiquidNitrogenLabelParam(deviceIdList);

        // 5.组装设备返回数据
        for(LiquidNitrogenDeviceVO deviceVO : list){
            Integer deviceAlarmStatus = 1;
            String deviceId = deviceVO.getDeviceId();
            List<DeviceMetadataParseInfo> parseInfoList = deviceMetadataParseInfoMap.get(deviceId);

            // 当前设备有最新上报数据
            if (CollUtil.isNotEmpty(parseInfoList)){
                List<PropertyModelVO> propertyList = new ArrayList<>(parseInfoList.size());

                for(DeviceMetadataParseInfo parseInfo : parseInfoList){
                    PropertyModelVO propertyModelVO = PropertyModelVO.builder().deviceId(deviceId)
                            .property(parseInfo.getProperty()).propertyName(parseInfo.getPropertyName())
                            .value(String.format("%.1f", parseInfo.getNumberValue())).unit(parseInfo.getUnit()).build();

                    // 计算液位的百分比
                    if (DevicePropertyConstants.liquid_nitrogen_depth.equals(parseInfo.getProperty())){
                        // 液氮罐液位额定深度的key，值为设备物模型中标签定义的key，需保持一致
                        BigDecimal depth = paramMap.get(deviceId + "-" + DevicePropertyConstants.liquid_nitrogen_rated_depth);
                        // depth不为空，并且大于0
                        if (ObjectUtil.isNotNull(depth) && depth.compareTo(BigDecimal.ZERO) == 1){
                            BigDecimal depthProportion = new BigDecimal(parseInfo.getNumberValue()).divide(depth, 5, BigDecimal.ROUND_HALF_UP);
                            propertyModelVO.setDepthProportion(depthProportion.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP));
                        }
                    }

                    // 判断属性是否报警
                    Integer status = ObjectUtil.isNotNull(deviceAlarmStatusMap.get(deviceId + "-" + parseInfo.getProperty())) ?
                            deviceAlarmStatusMap.get(deviceId + "-" + parseInfo.getProperty()) : 2;
                    propertyModelVO.setStatus(status);
                    propertyList.add(propertyModelVO);

                    // 0=报警中
                    if (Constants.ZERO.equals(status)){
                        deviceAlarmStatus = 0;
                    }
                }

                // 同一个设备，不同property的属性是同一次上报的
                deviceVO.setCreateTime(parseInfoList.get(0).getCreateTime());
                deviceVO.setPropertyModelVOList(propertyList);
            }

            // 设置设备的排序序号，排序规则：报警设备靠前，在线设备次之，离线最次
            if (Constants.ZERO.equals(deviceAlarmStatus)){
                deviceVO.setSort(0);
            }else {
                if (ObjectUtil.equals("online", deviceVO.getState())){
                    deviceVO.setSort(1);
                }
                if (ObjectUtil.equals("offline", deviceVO.getState())){
                    deviceVO.setSort(2);
                }
                if (ObjectUtil.equals("notActive", deviceVO.getState())){
                    deviceVO.setSort(3);
                }
            }
        }

        // 按sort升序排序，sort相同的按createTime降薪排序
        return list.stream().sorted(Comparator.comparing(LiquidNitrogenDeviceVO::getSort, Comparator.naturalOrder()))
                .collect(Collectors.toList());
    }


    /**
     * 批量查询每个设备在物模型的标签定义中设置的罐体深度和总容积    额定深度和额定容积配置在每个设备的物模型字段中
     * @param deviceIdList
     * @return  设备id-设备物模型中标签定义的key : 值
     *
     * "tags":[{"valueType":{"round":"HALF_UP","scale":2,"type":"float"},"name":"罐体深度","description":"500","id":"depth","expands":{"type":["read"]}},
     * {"valueType":{"round":"HALF_UP","scale":2,"type":"float"},"name":"罐体总容积","description":"2000","id":"volume","expands":{"type":["read"]}}]
     *
     */
    private Map<String, BigDecimal> queryLiquidNitrogenLabelParam(List<String> deviceIdList){
        List<DeviceInstance> deviceInstanceList = deviceInstanceService.listByIds(deviceIdList);
        if (CollUtil.isEmpty(deviceInstanceList)){
            return null;
        }
        Map<String, BigDecimal> map = new HashMap<>();

        for (DeviceInstance instance : deviceInstanceList){
            String deriveMetadata = instance.getDeriveMetadata();
            if (StringUtils.isBlank(deriveMetadata)){
               continue;
            }
            String tags = JSONObject.parseObject(deriveMetadata).getString("tags");
            if (StringUtils.isBlank(tags)){
                continue;
            }

            JSONArray array = JSONArray.parseArray(tags);
            for (int i = 0; i < array.size(); i++) {
                // 参数key和值
                JSONObject jsonObject = array.getJSONObject(i);

                String key = jsonObject.getString("id");
                if (StringUtils.isBlank(key)){
                    continue;
                }
                map.put(instance.getId() + "-" + key, new BigDecimal(jsonObject.getString("description")));
            }
        }
        return map;
    }


    /**
     * 驾驶舱氮气消耗情况(最近7天)  最近7天的液位深度情况(液位深度的消耗占比等于液氮容积的消耗占比)
     * @param dto
     * @return
     */
    public List<LiquidNitrogenUsageVO> queryUsage(LiquidNitrogenCockpitDTO dto){
        // 1.查询符合要求的设备
        LiquidNitrogenDeviceDTO deviceDTO = LiquidNitrogenDeviceDTO.builder()
                .paths(dto.getPaths()).departmentItemValue(dto.getDepartmentItemValue())
                .code(SpecialityModelCodeEnum.liquid_nitrogen.getCode()).build();
        List<LiquidNitrogenDeviceVO> deviceList = liquidNitrogenDeviceMapper.queryLiquidNitrogenDeviceByPage(null, deviceDTO);
        if (CollUtil.isEmpty(deviceList)){
            return buildObjectIfDataIsNull();
        }
        List<String> deviceIdList = deviceList.stream().map(LiquidNitrogenDeviceVO::getDeviceId).collect(Collectors.toList());
        // 2.查询每个设备的额度液位深度  设备id-设备物模型中标签定义的key : 值
        Map<String, BigDecimal> paramMap = queryLiquidNitrogenLabelParam(deviceIdList);

        // 按产品id分组，并将同一产品id的设备id放在同一个list中
        Map<String, List<String>> deviceMap = deviceList.stream().collect(
                Collectors.groupingBy(LiquidNitrogenDeviceVO::getProductId, Collectors.mapping(LiquidNitrogenDeviceVO::getDeviceId, Collectors.toList())));

        List<LiquidNitrogenDeviceHistoryDataVO> historyDataList = new ArrayList<>();
        // 3.查询液位深度的历史上报数据
        dto.setEndTime(DateUtil.getYmdHms(DateUtil.getYmd() + " 23:59:59"));
        dto.setStartTime(DateUtil.getYmdHms(DateUtil.getAfterDate(DateUtil.getYmd(), -6, DateUtil.DEFAULT_FORMAT_DATE) + " 00:00:00"));
        dto.setProperty(DevicePropertyConstants.liquid_nitrogen_depth);
        deviceMap.forEach((key, value) -> {
            // 获取当前设备的历史检测数据所在的TDengine表名
            String tableName = String.format("properties_%S", key);
            //判断表是否存在
            Boolean existStable = taosService.checkStable("jetlinks_clean_room", tableName);
            if (existStable){
                dto.setDeviceIdList(value);
                List<LiquidNitrogenDeviceHistoryDataVO> list = liquidNitrogenCockpitMapper.queryHistoryData(dto, "jetlinks_clean_room." + tableName);
                if (CollUtil.isNotEmpty(list)) {
                    historyDataList.addAll(list);
                }
            }
        });

        // 4.整理数据
        if (CollUtil.isEmpty(historyDataList)){
            return buildObjectIfDataIsNull();
        }
        List<LiquidNitrogenUsageVO> dataList = new ArrayList<>();

        // 按设备id分组， 每个设备的额定深度、额定容积会存在差异
        Map<String, List<LiquidNitrogenDeviceHistoryDataVO>> historyDataGroupByDeviceIdMap = historyDataList.stream().collect(Collectors.groupingBy(p -> p.getDeviceId()));
        for (Map.Entry<String, List<LiquidNitrogenDeviceHistoryDataVO>> entry : historyDataGroupByDeviceIdMap.entrySet()) {
            // 额定深度
            BigDecimal depth = paramMap.get(entry.getKey() + "-" + DevicePropertyConstants.liquid_nitrogen_rated_depth);
            // 额定容积
            BigDecimal volume = paramMap.get(entry.getKey() + "-" + DevicePropertyConstants.liquid_nitrogen_rated_volume);

            // 把当前设备的数据按天分组，计算每天的使用量
            Map<String, List<LiquidNitrogenDeviceHistoryDataVO>> deviceDataVoGroupByDayMap = entry.getValue().stream()
                    .collect(Collectors.groupingBy(p -> p.getBelongingTime()));
            for (Map.Entry<String, List<LiquidNitrogenDeviceHistoryDataVO>> everyDayDataEntry : deviceDataVoGroupByDayMap.entrySet()){

                // 把当前belongingTime所属时间内的数据按上报时间升序排序
                List<LiquidNitrogenDeviceHistoryDataVO> dataVOListByDevice = everyDayDataEntry.getValue().stream()
                        .sorted(Comparator.comparing(LiquidNitrogenDeviceHistoryDataVO::getCreateTime, Comparator.naturalOrder()))
                        .collect(Collectors.toList());

                // 计算每个设备每天的使用量     计算每相邻的两条数据间，液位深度的差值，并计算使用的液氮量(消耗的液位深度占比等于消耗的容积占比)
                BigDecimal dayTotal = new BigDecimal("0.00");
                for (int i = 1; i < dataVOListByDevice.size(); i++){
                    if (dataVOListByDevice.get(i).getNumberValue() < dataVOListByDevice.get(i-1).getNumberValue()){

                        BigDecimal difference = new BigDecimal(dataVOListByDevice.get(i-1).getNumberValue())
                                .subtract(new BigDecimal(dataVOListByDevice.get(i).getNumberValue()));
                        // depth不为空，并且大于0
                        if (ObjectUtil.isNotNull(depth) && depth.compareTo(BigDecimal.ZERO) == 1){
                            // 深度占比
                            BigDecimal depthProportion = difference.divide(depth, 5, BigDecimal.ROUND_HALF_UP);
                            dayTotal = dayTotal.add(volume.multiply(depthProportion));
                        }

                    }else {
                        // 后一个值比上一个大，说明给液氮罐加液氮了；相等则说明没有使用
                        continue;
                    }
                }
                LiquidNitrogenUsageVO deviceEveryDayUsage = LiquidNitrogenUsageVO.builder()
                        .deviceId(entry.getKey()).belongingTime(everyDayDataEntry.getKey())
                        .usage(dayTotal).build();
                dataList.add(deviceEveryDayUsage);
            }
        }

        // 按日期分组
        Map<String, List<LiquidNitrogenUsageVO>> usageGroupByDayMap = dataList.stream().collect(Collectors.groupingBy(p -> p.getBelongingTime()));
        // 获取指定时间段内的每一天
        List<LocalDate> dayList = DateUtil.getDatesInRange(DateUtil.convertToLocalDate(dto.getStartTime()), DateUtil.convertToLocalDate(dto.getEndTime()));

        List<LiquidNitrogenUsageVO> voList = new ArrayList<>(dayList.size());
        dayList.forEach(day -> {

            List<LiquidNitrogenUsageVO> usageVOList = usageGroupByDayMap.get(DateUtil.getFormatDate(day, DateUtil.DEFAULT_FORMAT_DATE));
            BigDecimal totalUsage = new BigDecimal("0.00");
            if (CollUtil.isNotEmpty(usageVOList)){
                totalUsage = usageVOList.stream().map(LiquidNitrogenUsageVO::getUsage).reduce(BigDecimal.ZERO, BigDecimal::add);
            }

            LiquidNitrogenUsageVO liquidNitrogenUsageVO = LiquidNitrogenUsageVO.builder()
                    .usage(totalUsage.setScale(2, BigDecimal.ROUND_HALF_UP)).createTime(day).build();
            voList.add(liquidNitrogenUsageVO);
        });
        // 按时间升序排序
        return voList.stream().sorted(Comparator.comparing(LiquidNitrogenUsageVO::getCreateTime, Comparator.naturalOrder()))
                .collect(Collectors.toList());
    }


    /**
     * 当没有对应的历史数据时，返回一个数值为0的集合给前端渲染图表
     * @return
     */
    private List<LiquidNitrogenUsageVO> buildObjectIfDataIsNull(){
        Date endTime = DateUtil.getYmdHms(DateUtil.getYmd() + " 23:59:59");
        Date startTime = DateUtil.getYmdHms(DateUtil.getAfterDate(DateUtil.getYmd(), -6, DateUtil.DEFAULT_FORMAT_DATE) + " 00:00:00");
        // 获取指定时间段内的每一天
        List<LocalDate> dayList = DateUtil.getDatesInRange(DateUtil.convertToLocalDate(startTime), DateUtil.convertToLocalDate(endTime));
        List<LiquidNitrogenUsageVO> voList = new ArrayList<>(dayList.size());
        dayList.forEach(day -> {
            LiquidNitrogenUsageVO liquidNitrogenUsageVO = LiquidNitrogenUsageVO.builder()
                    .usage(new BigDecimal("0.00")).createTime(day).build();
            voList.add(liquidNitrogenUsageVO);
        });
        return voList.stream().sorted(Comparator.comparing(LiquidNitrogenUsageVO::getCreateTime, Comparator.naturalOrder()))
                .collect(Collectors.toList());
    }


}
